/*
 * Netcp.c
 *
 *  Created on: Oct 14, 2018
 *      Author: ax
 */
#define USE_ETH_OOB
#define USE_EPIB_WORD

#include <Log.h>
#include <Device.h>
#include <Hw/Psc.h>
#include <Hw/Qmss.h>
#include <Hw/Cpdma.h>
#include <Hw/Netcp.h>
#include <Hw/Serdes.h>
#include <Hw/Memory.h>
#include <Socket/Ipnet.h>
#include <Socket/Link/Ethernet.h>

#include <c6x.h>

#include <ti/csl/cslr_pa_ss.h>

#include <ti/csl/csl_cpsw.h>
#include <ti/csl/csl_cpswAux.h>
#include <ti/csl/csl_mdioAux.h>
#include <ti/csl/csl_cpsgmiiAux.h>
#include <ti/csl/csl_bootcfgAux.h>
#include <ti/csl/csl_cpgmac_slAux.h>


/* Holds the base address of ports. */
#if defined(SOC_K2K)||defined(SOC_K2H)||defined(SOC_C6678)
void *cpsgmiiPortBaseAddr[4] = {
    &hCpSgmiiRegs->SGMII_PORT1,
    &hCpSgmiiRegs->SGMII_PORT2,
    &hCpSgmiiRegs->SGMII_PORT3,
    &hCpSgmiiRegs->SGMII_PORT4 };
#elif defined(SOC_K2G)
void *cpsgmiiPortBaseAddr[8] = {
    &hCpSgmiiRegs->SGMII_PORT[0],
    &hCpSgmiiRegs->SGMII_PORT[1],
    &hCpSgmiiRegs->SGMII_PORT[2],
    &hCpSgmiiRegs->SGMII_PORT[3],
    &hCpSgmiiRegs->SGMII_PORT[4],
    &hCpSgmiiRegs->SGMII_PORT[5],
    &hCpSgmiiRegs->SGMII_PORT[6],
    &hCpSgmiiRegs->SGMII_PORT[7] };
#else
#error NO SOC defined || SOC not supported!
#endif

Uint8 __buffer(Netcp) Netcp_Buffer[Netcp_BDC_ALL][Netcp_BUFFER_SIZE];


void __init Mdio_init(void) {
    hMdioRegs->CONTROL_REG
        = CSL_FMK(MDIO_CONTROL_REG_ENABLE, 1)
        | CSL_FMK(MDIO_CONTROL_REG_FAULT, 1)
        | CSL_FMK(MDIO_CONTROL_REG_CLKDIV, 140)
        | CSL_FMK(MDIO_CONTROL_REG_FAULT_DETECT_ENABLE, 1);
    #if 0
    hMdioRegs->USER_GROUP[0].USER_PHY_SEL_REG
        = CSL_FMK(MDIO_USER_PHY_SEL_REG_LINKINT_ENABLE, 1)
        | CSL_FMK(MDIO_USER_PHY_SEL_REG_PHYADR_MON, 0);
    hMdioRegs->USER_GROUP[1].USER_PHY_SEL_REG
        = CSL_FMK(MDIO_USER_PHY_SEL_REG_LINKINT_ENABLE, 1)
        | CSL_FMK(MDIO_USER_PHY_SEL_REG_PHYADR_MON, 1);
    if (cnt) {
        for (short e = 0; e < cnt; e++) {
        /*The MDIO module powers up in an idle state before it is enabled.
        wait for it is not idle (really enabled)*/
        while(CSL_FEXT(hMdioRegs->CONTROL_REG, MDIO_CONTROL_REG_IDLE));
    }
    #else
    TODO("configure PHY if necessary");
    #endif
}


void __init
Cpsw_config(
Uint16 mru
){
    CSL_CPSW_setPort0RxMaxLen(mru);

    CSL_CPSW_CONTROL ctl = {.p0Enable = YES};
    CSL_CPSW_setCpswControlReg(&ctl);

    //enable all
    CSL_CPSW_PORTSTAT portStatsCfg = {
                        .p0AStatEnable  =   1,
                        .p0BStatEnable  =   1,
                        .p1StatEnable   =   1,
                        .p2StatEnable   =   1 };
    CSL_CPSW_setPortStatsEnableReg(&portStatsCfg);


    /* Reset and enable the ALE */
    hCpsw3gfRegs->ALE_CONTROL_REG
        = CSL_FMK(CPSW_3GF_ALE_CONTROL_REG_ENABLE_ALE, YES)
        | CSL_FMK(CPSW_3GF_ALE_CONTROL_REG_CLEAR_TABLE, YES);

    /* FIFO Blocks for each port, 20 in total  */
    for (short p = 0; p < Netcp_PORT_ALL; p++)
        CSL_CPSW_nGF_setPortMaxBlocksReg(p, 20 - Netcp_TX_THRESHOLD,
                                                 Netcp_TX_THRESHOLD);

    /* All ports put into forward mode */
    CSL_CPSW_ALE_PORTCONTROL portControlInfo = {
            .portState = ALE_PORTSTATE_FORWARD };
    for (short p = 0; p < Netcp_CPSW_PORT_ALL; p++)
        CSL_CPSW_setAlePortControlReg(p, &portControlInfo);
}


#define DEVICE_PA_NUM_PDSPS                 6
#define Netcp_PDSP_USED                     0
#define DEVICE_PA_RUN_CHECK_COUNT           100         /* Number of loops to verify PA firmware is running */

#define PA_REG_VAL_PDSP_CTL_DISABLE_PDSP    0
#define PA_NUM_MAILBOX_SLOTS                4


/* This array contains the PDSP instructions which are to be    */
/* down loaded from the host CPU to the PDSP instruction memory.*/
/* This array is generated by the PDSP assembler from RBL.      */
const static unsigned int __firmware(pa) PDSPcode[] =  {
        0x2eff9196, 0x85002096, 0x0101f6f6, 0x81002496,
        0xcf04fffe, 0x2e808f86, 0x240cecc2, 0x2411e082,
        0x68e2ec05, 0x59108926, 0x24002104, 0x2f000384,
        0x21000200, 0x0101f7f7, 0x81042497, 0x24000c04,
        0x2f000384, 0x2e808f86, 0x24000004, 0x240020c4,
        0x2f000384, 0x2e808f8e, 0x68e6fb04, 0x68c7dc03,
        0x0101f8f8, 0x21002400, 0x68e6fd04, 0x68c7de03,
        0x0101f9f9, 0x21002400, 0x0101fafa, 0x810c249a,
        0x24002104, 0x2f000384, 0x8700e286, 0x21000200,
        0x00f9f8e1, 0x81082481, 0x24002004, 0x24000644,
        0x24000064, 0x109e9ec5, 0x2400b024, 0x24000005,
        0x2f000384, 0x8700e186, 0x21000200, 0x24000c04,
        0x2f000384, 0x2e80878e, 0x10eeeefb, 0x10efeffc,
        0x10f0f0fd, 0x10f1f1fe, 0x24002104, 0x2f000384,
        0x21000200                                      };

Netcp_Status
__init
Pa_setup(
    uint8_t* mac
){
    short i;
    CSL_Pa_ssRegs* const hPa = (CSL_Pa_ssRegs*) CSL_NETCP_CFG_REGS;

    /* Disable all PDSPs */
    for (i = 0; i < DEVICE_PA_NUM_PDSPS; i++)
        hPa->PDSP_CTLSTAT[i].PDSP_CONTROL = PA_REG_VAL_PDSP_CTL_DISABLE_PDSP;
    /* Clear the mailbox registers for PDSP 0 */
    for (i = 0; i < PA_NUM_MAILBOX_SLOTS; i++)
        hPa->MAILBOX[Netcp_PDSP_USED].MBOX_SLOT[i] = 0;
    /* Give a few cycles for the disable */
    nop (800);
    /* download the firmware */
    memcpy(&hPa->PDSP_IRAM[Netcp_PDSP_USED], PDSPcode, sizeof(PDSPcode));
    /* Reset the PC and enable PDSP0 */
    hPa->PDSP_CTLSTAT[Netcp_PDSP_USED].PDSP_CONTROL
        = CSL_FMK(PA_SS_PDSP_CONTROL_SOFT_RST_N, YES)
        | CSL_FMK(PA_SS_PDSP_CONTROL_PDSP_ENABLE, YES)
        | CSL_FMKT(PA_SS_PDSP_CONTROL_PCOUNTER_RST_VAL, RESETVAL);


    /* Form the configuration command in a buffer linked to a descriptor */
    HostDesc hd = (HostDesc) Qmss_pop(Netcp_FDQ);
    PaCfg cfg = (PaCfg) hd->orig_buff0_ptr;

    /* Filter everything except the desired mac address and the broadcast mac*/
    for (i = 0; i < Eth_MAC_SIZE; i++) {
        cfg->mac0[i] = *mac++;
        cfg->mac1[i] = 0xff;
    }
    cfg->mac0[Eth_MAC_SIZE] = cfg->mac0[Eth_MAC_SIZE + 1] = 0;
    //rxQueue
    cfg->rxQnumH = Netcp_RXQ >> 8;
    cfg->rxQnumL = Netcp_RXQ & 0xff;

    //descriptor fill
    hd->softwareInfo0 = PA_MAGIC_ID;
    hd->buffer_len = sizeof(paConfig);
    hd->packet_length = sizeof(paConfig);


    Netcp_Status status = Netcp_PA_PDSP_FAULT;
    /* loop verify that the mailboxes have been cleared */
    for (i = 0; ((i < DEVICE_PA_RUN_CHECK_COUNT) && (status != Netcp_SOK)); i++)  {
        nop(800);
        status = (Netcp_Status) hPa->MAILBOX[Netcp_PDSP_USED].MBOX_SLOT[3];
    }
    /* Send the command to the PA through the QM */
    if (status == Netcp_SOK) {
        Qmss_push(Netcp_QPDSP0, (Descriptor) hd | Netcp_DescSize);

        #define Netcp_PA_PSSW_ROUTE_PDSP0      0
        /* Streaming switch configuration : route all packets to PDSP0  */
        hPa->STREAMSWITCH.STREAMSWITCH_SOFT_RESET = Netcp_PA_PSSW_ROUTE_PDSP0;

        while (Netcp_RXBD_CNT != Qmss_getEntryCnt(Netcp_FDQ));
        hd->softwareInfo0 = 0;
        hd->buffer_len = hd->packet_length = Eth_MTU;

        return status;
    }

    Log_defect("[Netcp]: !!! PASS's Pdsp setup mailbox failed.\n");
    return Netcp_PA_PDSP_FAULT;
}


#define Sgmii_TXCFG             0x00010881
#define Sgmii_RXCFG             0x00700601
#define Sgmii_AUXCFG            0x00000229

#define Sgmii_ADV_MAC2_PHY      0x00000001  /* Mr_Adv_Ability: mac to phy config */
#define Sgmii_ADV_FD_GIE        0x00009801  /* Mr_Adv_Ability: advertise fullduplex gigabit */

#define Sgmii_AN_MASTER         0x00000021  /* Control: enable auto-negotiation and master mode */
#define Sgmii_AN_SLAVE          0x00000001  /* Control: enable auto-negotiation and slave mode */

void
__init
Sgmii_setup(
    Uint16 port,
    Uint32 mtu,
    Uint32 ctlCfg
){
    Uint32 i, tmp;
    CSL_CpsgmiiRegs* const hSgmii
        = (CSL_CpsgmiiRegs*) cpsgmiiPortBaseAddr[port];


    /* Disable negotiation */
    hSgmii->CONTROL_REG = 0;

    /* set TX/RX/AUX configuration */
    CSL_SGMII_setTxConfig(port, Sgmii_TXCFG);
    CSL_SGMII_setRxConfig(port, Sgmii_RXCFG);
    CSL_SGMII_setAuxConfig(port, Sgmii_AUXCFG);

    /* wait for the SerDes pll to lock,
     * but don't trap if lock is never read */
    for (i = 0; i < 1500; i++) {
        nop(1000);
        if (YES == CSL_FEXT(hSgmii->STATUS_REG, CPSGMII_STATUS_REG_LOCK))
            break;
    }
    Log_mark("[Sgmii]: port-%d %s @%d.\n",
             port, (i >= 1000) ? "XXX UNLOCKED!" : "locked", i);

    /* Configure SGMII Advertised ability info */
    CSL_SGMII_ADVABILITY sgmiiAdvAbility = {
        .bLinkUp     =   YES,
        .duplexMode  =   CSL_SGMII_FULL_DUPLEX,
        .linkSpeed   =   CSL_SGMII_1000_MBPS    };
    CSL_SGMII_setAdvAbility (port, &sgmiiAdvAbility);

    /* Control */
    hSgmii->CONTROL_REG = ctlCfg;

    /* Auto negotiation check & return configuration for MAC */
    tmp = CSL_FMK(CPSGMII_STATUS_REG_LINK, YES);
    if (ctlCfg & CSL_CPSGMII_CONTROL_REG_MR_AN_ENABLE_MASK)
        tmp |= CSL_FMK(CPSGMII_STATUS_REG_MR_AN_COMPLETE, YES);

    for (i = 0; i < 2500; i++) {//250 ms timeout
        nop(100000);            //0.1 ms delay
        if (tmp == (hSgmii->STATUS_REG & tmp))
            break;
    }

    if (i >= 2500)
        tmp = CSL_FMK(CPGMAC_SL_MACCONTROL_REG_GIG, YES)
            | CSL_FMK(CPGMAC_SL_MACCONTROL_REG_FULLDUPLEX, YES)
            | CSL_FMK(CPGMAC_SL_MACCONTROL_REG_GMII_EN, YES);
    else
        tmp = CSL_FMK(CPGMAC_SL_MACCONTROL_REG_CTL_EN, YES)
            | CSL_FMK(CPGMAC_SL_MACCONTROL_REG_FULLDUPLEX, YES)
            | CSL_FMK(CPGMAC_SL_MACCONTROL_REG_GMII_EN, YES);

    Log_mark("[Sgmii]: port-%d %s @%d.\n",
             port, (i < 2500) ? "link up" : "!!NO-LINK!!", i);

    /* MAC configuration */
    CSL_CPGMAC_SL_setRxMaxLen(port, mtu);
    CSL_CPGMAC_SL_setMacControlReg(port, tmp);
}

#ifndef Sgmii_Serdes_CFGPLL
#define Sgmii_Serdes_CFGPLL     0x41
#endif
#define Sgmii_Serdes_CFGTX      0x000108a1
#define Sgmii_Serdes_CFGRX      0x00700621


static void
Sgmii_serdesInit(
    Uint32 pll
){
    CSL_BootCfgUnlockKicker();

    if (hBootCfg->SGMII_CFGPLL != pll) {
        hBootCfg->SGMII_CFGPLL |= (1 << Serdes_PLL_CFG_ENPLL_SHIFT);
        nop(1000);
    }

    hBootCfg->SGMII_CFGPLL = pll;
    nop(1600000);

    hBootCfg->SGMII_CFGTX0 = Sgmii_Serdes_CFGTX;
    hBootCfg->SGMII_CFGRX0 = Sgmii_Serdes_CFGRX;
    hBootCfg->SGMII_CFGTX1 = Sgmii_Serdes_CFGTX;
    hBootCfg->SGMII_CFGRX1 = Sgmii_Serdes_CFGRX;

    CSL_BootCfgLockKicker();
}



Netcp_Status
__init
Netcp_init(
    Bits16   portMask,
    Size     mtu,
    Loopback loopback
){
    if (Psc_SOK != Psc_moduleEnable(CSL_PSC_PD_PASS, CSL_PSC_LPSC_PA))
        return Netcp_PWR_FAIL;
    if (Psc_SOK != Psc_moduleEnable(CSL_PSC_PD_PASS, CSL_PSC_LPSC_CPGMAC))
        return Netcp_PWR_FAIL;

    /* SerDes configure */
    Sgmii_serdesInit(Sgmii_Serdes_CFGPLL);


    /* Reset & setup the SGMIIs */
    // Reset EMAC port & wait reset complete
    CSL_CPGMAC_SL_resetMac(Netcp_PORTA);
    CSL_CPGMAC_SL_resetMac(Netcp_PORTB);
    while (CSL_CPGMAC_SL_isMACResetDone(Netcp_PORTA) != TRUE);
    while (CSL_CPGMAC_SL_isMACResetDone(Netcp_PORTB) != TRUE);

    // Reset SGMII port & wait reset complete
    CSL_SGMII_doSoftReset(Netcp_PORTA);
    CSL_SGMII_doSoftReset(Netcp_PORTB);
    while (CSL_SGMII_getSoftResetStatus (Netcp_PORTA));
    while (CSL_SGMII_getSoftResetStatus (Netcp_PORTB));

    //SGMII setup
    Sgmii_setup(Netcp_PORTA, mtu, Sgmii_AN_SLAVE);
    Sgmii_setup(Netcp_PORTB, mtu, Sgmii_AN_SLAVE);


    /* Configure the internal switch */
    Cpsw_config(mtu);

    return Netcp_SOK;
}


static void Netcp_descriptorInit(Descriptor desc) {
    HostDesc hd = (HostDesc) desc;

    PtrVal pv = hd->buffer_ptr;
    assert(pv == ROUND_UP(pv, Eth_BUFFER_ALIGN));
    hd->buffer_ptr = hd->orig_buff0_ptr = (pv | Eth_BUFFER_ALIGN);//skip OOB

    EthFrame efrm = (EthFrame) pv;
    efrm->Oob.Ext.Descriptor= (PtrVal) hd | Netcp_DescSize;
    efrm->Oob.Ext.Magic     = efrm;
}


void
Netcp_setup(
    void
){
    short i;
    Cpdma_globalEnable(CpDma_PASS, 5, 5, false);

    for (i = 0; i < Cpdma_PASS_MAX_RX_CH_NUM; i++)
        Cpdma_rxChannelEnable(CpDma_PASS, i);

    for (i = 0; i < Cpdma_PASS_MAX_TX_CH_NUM; i++)
        Cpdma_txChannelEnable(CpDma_PASS, i);

    Cpdma_RxFlowConfiguration rxFlow = {
          .dest_qnum = Netcp_RXQ,
          .desc_type = 1,
          .einfo_present = 1,
          .psinfo_present = 1,
          .fdq0_sz0_qnum = Netcp_FDQ,
          .fdq0_sz1_qnum = Netcp_FDQ,
          .fdq1_qnum = Netcp_FDQ,
          .fdq2_qnum = Netcp_FDQ,
          .fdq3_qnum = Netcp_FDQ };
    for (i = 0; i < Cpdma_PASS_MAX_RX_FLOW_NUM; i++)
        Cpdma_rxFlowConfig(CpDma_PASS, &rxFlow, i);

    //TCQ
    Qmss_queueSetup(Netcp_TCQ,                      //Queue     dstQ,
                    Queue_HPOOL(Netcp_DescSize),    //Queue     srcQ,
                    Netcp_TXBD_CNT,                 //Uint16    num,
                    Netcp_DescSize,                 //DescSize  fsize,
                    true,                           //bool      epib,
                    Netcp_PS_DATA_WORD,             //Uint8     psSize,
                    &Netcp_Buffer[0][0],            //Uint8*    heaps,
                    Netcp_BUFFER_SIZE,              //Uint32    buffSize
                    Netcp_descriptorInit);
    //FDQ
    Qmss_queueSetup(Netcp_FDQ,                      //Queue     dstQ,
                    Queue_HPOOL(Netcp_DescSize),    //Queue     srcQ,
                    Netcp_RXBD_CNT,                 //Uint16    num,
                    Netcp_DescSize,                 //DescSize  fsize,
                    true,                           //bool      epib,
                    Netcp_PS_DATA_WORD,             //Uint8     psSize,
                    &Netcp_Buffer[Netcp_TXBD_CNT][0],//Uint8*    heaps,
                    Netcp_BUFFER_SIZE,              //Uint32    buffSize
                    Netcp_descriptorInit);

    //Use SOC's MAC address
    Uint32 macA = hBootCfg->EMAC_ID1;
    Uint32 macB = hBootCfg->EMAC_ID2;

    Eth_ARP_TABLE[_SELF].Mac.Hex[0] = (macB >>  8) & 0xff;
    Eth_ARP_TABLE[_SELF].Mac.Hex[1] = (macB >>  0) & 0xff;
    Eth_ARP_TABLE[_SELF].Mac.Hex[2] = (macA >> 24) & 0xff;
    Eth_ARP_TABLE[_SELF].Mac.Hex[3] = (macA >> 16) & 0xff;
    Eth_ARP_TABLE[_SELF].Mac.Hex[4] = (macA >>  8) & 0xff;
    Eth_ARP_TABLE[_SELF].Mac.Hex[5] = (macA >>  0) & 0xff;

    Pa_setup(&Eth_ARP_TABLE[_SELF].Mac.Hex[0]);
}

